using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.IO;

namespace Umdf.SideShow.PocketPC
{
    public class DataReceivedEventArgs : EventArgs
    {
        private byte[] _buffer;
        
        public DataReceivedEventArgs(byte[] buffer)
        {
            _buffer = buffer;
        }

        public byte[] Buffer
        {
            get { return _buffer; }
        }
    }

    public abstract class PocketPCChannelBase : IDisposable
    {
        private const int BufferSize = 4096;

        private TcpClient _client;
        private NetworkStream _ns;
        private byte[] _buffer = new byte[BufferSize];

        public event EventHandler<DataReceivedEventArgs> DataReceived;
        public event EventHandler Closed;

        protected PocketPCChannelBase()
        {
            _client = new TcpClient();
        }

        protected PocketPCChannelBase(TcpClient client)
        {
            _client = client;
            _ns = _client.GetStream();
            _ns.BeginRead(_buffer, 0, BufferSize, ReadCallback, null);
        }

        ~PocketPCChannelBase()
        {
            Dispose();
        }

        public bool Connected
        {
            get
            {
                return _client != null && _client.Connected;
            }
        }

        public void Open(IPAddress host, int port)
        {
            _client.Connect(host, port);
            _ns = _client.GetStream();
            _ns.BeginRead(_buffer, 0, BufferSize, ReadCallback, null);
        }

        public void Close()
        {
            _client.Close();
        }

        public void Send(byte[] buf)
        {
            if (_client.Connected)
            {
                _ns.Write(buf, 0, buf.Length);
            }
        }

        private void ReadCallback(IAsyncResult result)
        {
            if (!result.IsCompleted)
            {
                return;
            }
            try
            {
                int read = _ns.EndRead(result);
                using (MemoryStream ms = new MemoryStream())
                {
                    ms.Write(_buffer, 0, read);
                    if (read == BufferSize)
                    {
                        while (_ns.DataAvailable)
                        {
                            read = _ns.Read(_buffer, 0, BufferSize);
                            ms.Write(_buffer, 0, read);
                        }
                    }
                    OnDataReceived(ms.ToArray());
                    _ns.BeginRead(_buffer, 0, BufferSize, ReadCallback, null);
                }
            }
            catch(IOException)
            {
                this.Close();
                OnClosed();
            }
        }

        protected virtual void OnClosed()
        {
            if (Closed != null)
            {
                Closed(this, EventArgs.Empty);
            }
        }

        protected virtual void OnDataReceived(byte[] data)
        {
            if (DataReceived != null)
            {
                DataReceived(this, new DataReceivedEventArgs(data));
            }
        }


        #region IDisposable Members

        public void Dispose()
        {
            Close();
            GC.SuppressFinalize(this);
        }

        #endregion
    }

}
